R"""JsonT transformer.

Transforms JSON + template in anything::

    >>> template = { "self": "<table>{pnt}</table>",
    ...              "pnt" : "<tr><td>{pnt.x}</td><td>{pnt.y}</td></tr>" }
    >>> data = { "pnt": { "x": 2, "y": 3 } }
    >>> print jsont(data, template)
    <table><tr><td>2</td><td>3</td></tr></table>

    >>> template = { "self": "<table><tr>{$}</tr></table>",
    ...              "self[*]": "<td>{$}</td>" }
    >>> data = [ 1, 2 ]
    >>> print jsont(data, template)
    <table><tr><td>1</td><td>2</td></tr></table>

    >>> template = { "self": "<table>\n{$}\n</table>",
    ...              "self[*]": "<tr>{$}</tr>\n",
    ...              "self[*][*]": "<td>{$}</td>" }
    >>> data = [[1,2],[3,4]]
    >>> print jsont(data, template)
    <table>
    <tr><td>1</td><td>2</td></tr>
    <tr><td>3</td><td>4</td></tr>
    <BLANKLINE>
    </table>

    >>> template = { "self": "<div>\n{p}\n</div>",
    ...              "p": "<table><tr>{$}</tr></table>\n",
    ...              "p[*]": "<td>{$.x}</td><td>{$.y}</td>" }
    >>> data  = {"a": "hello", "p": [{"x": 1,"y": 2}, {"x": 3, "y": 4}]}
    >>> print jsont(data, template)

"""

import re
import sys


TRANSFORMATION_FUNCTION = re.compile(r"{([\w\$\.\[\]\'@\(\)]+)}")


def jsont(data, rules, expr='self'):
    # Normalize rules by adding the ``self`` prefix where necessary.
    for rule in rules:
        if not rule.startswith('rule'):
            rules['self.' + rule] = rules[rule]
            del rules[rule]

    def trf(s):
        return TRANSFORMATION_FUNCTION.sub(lambda m, expr=expr: process(m.group(1), expr, data, rules), s)

    x = re.sub("\[\d+?\]", "[*]", expr)
    if x in rules:
        if rules[x].startswith('function'):
            return trf(rules[x](resolve(expr, data, rules), expr))
        else:
            return trf(rules[x], expr)
    else:
        return resolve(expr, data, rules)


def expand(arg, expr):
    expr = arg.replace("$", expr)
    if not expr.startswith('self.'): expr = 'self.' + expr
    return expr


def process(arg, expr, data, rules):
    if arg.startswith("@"):
        return resolve(re.sub(r"@(\w+)\(([\w\$\.\[\]\']+)\)",
            lambda m: "rules['self.%s'](%s)" % (m.group(1), expand(m.group(2), expr))), data, rules)
    elif arg != '$':
        return jsont(data, rules, expr=expand(arg, expr))
    else:
        return resolve(expr, data, rules)


def resolve(expr, data, rules):
    out = []
    items = expr.split('.')
    var = {'self': data}
    for item in items:
        slices = re.findall(r"\[(\d+?)\]", item)
        if slices:
            slices = map(int, slices)
            slices.insert(0, item)
            print >> sys.stderr, slices, item
            item = reduce(lambda v, k: v[k], slices)
        var = var[item]

    if isinstance(var, dict):
        for key in var: out.append(jsont(data, rules, expr=expr + '.' + key))
    elif isinstance(var, list):
        for i in range(len(var)):
            out.append(jsont(data, rules, expr=expr + "[" + str(i) + "]"))
    else:
        out.append(var)
    return ''.join(var)


def _test():
    import doctest
    doctest.testmod()

if __name__ == "__main__":
    _test()

